
// Copyright (C) 2009-2011 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//  
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;

namespace Derm.Render
{
	/// <summary>
	/// Render pipeline. 
	/// </summary>
	/// <remarks>
	/// <para>
	/// A RenderPipeline define a rendering operation, and it has associated the following classes
	/// for achieving the result:
	/// - A RenderSurface, which it will hold the rendering results.
	/// - An ICamera, which defined the model and projection matriced used for projecting primitives
	///   on the surface.
	/// - A RenderHierarchy which collects the primitives to be rendered.
	/// </para>
	/// </remarks>
	public abstract class RenderPipeline
	{
		#region Constructors
		
		/// <summary>
		/// RenderPipeline constructor. 
		/// </summary>
		/// <param name="rSurface">
		/// A <see cref="RenderSurface"/> where the pipeline is executed on.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// This exception is thrown if the parameter <paramref name="rSurface"/> is null.
		/// </exception>
		protected RenderPipeline(RenderSurface rSurface)
		{
			if (rSurface == null)
				throw new ArgumentNullException("rSurface");
			
			// Store surface
			mSurface = rSurface;
		}
		
		#endregion

		#region RenderViewport Management
		
		/// <summary>
		/// RenderViewport of this RenderPipeline.
		/// </summary>
		public RenderViewport SurfaceViewport
		{
			get { return (mViewport); }
			set {
				mViewport = value;
			}
		}
		
		/// <summary>
		/// RenderViewport of this RenderPipeline.
		/// </summary>
		private RenderViewport mViewport = new RenderViewport(0.0f, 0.0f, 1.0f, 1.0f);
		
		#endregion

		#region Rendering Management

		/// <summary>
		/// Surface where rendering results are directed. 
		/// </summary>
		public RenderSurface Surface { get { return (mSurface); } }

		/// <summary>
		/// Enabled status.
		/// </summary>
		/// <remarks>
		/// This property determine whether this RenderPipeline shall be executed. By default,
		/// this property has the value 'true'. In the case its value is 'false', this
		/// RenderPipeline will not be executed.
		/// </remarks>
		public virtual bool Enabled
		{
			get { return (mEnabled); }
			set { mEnabled = value; }
		}

		/// <summary>
		/// Initialize this RenderPipeline.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for initialization.
		/// </param>
		public virtual void InitializePipeline(RenderContext ctx)
		{
			
		}

		/// <summary>
		/// Reset this RenderPipeline.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for initialization.
		/// </param>
		internal virtual void ResetPipeline(RenderContext ctx)
		{
			ResetThisPipeline(ctx);
		}

		/// <summary>
		/// Reset this concrete RenderPipeline.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for initialization.
		/// </param>
		protected virtual void ResetThisPipeline(RenderContext ctx)
		{
			
		}

		/// <summary>
		/// Execute render pipeline. 
		/// </summary>
		/// <param name="rContext">
		/// A <see cref="RenderContext"/> used for rendering.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// This exception is thrown if the parameter <paramref name="rContext"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// This exception is thrown if the parameter <paramref name="rContext"/> is not current to calling thread.
		/// </exception>
		internal void ExecutePipeline(RenderContext rContext)
		{
			if (rContext == null)
				throw new ArgumentNullException("rContext");
			if (rContext.IsCurrent() == false)
				throw new ArgumentException("not current", "rContext");

			// Define pipeline viewport
			Derm.OpenGL.Gl.Viewport(
				SurfaceViewport.GetLeft(mSurface), SurfaceViewport.GetBottom(mSurface),
				SurfaceViewport.GetRight(mSurface), SurfaceViewport.GetTop(mSurface)
				);
			RenderException.DebugCheckErrors();

			// Execute concrete pipeline implementatioon
			ExecThisPipeline(rContext);
		}

		/// <summary>
		/// Concrete render pipeline implementation
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for rendering.
		/// </param>
		/// <remarks>
		/// <para>
		/// This method define the pipeline operations.
		/// </para>
		/// </remarks>
		protected abstract void ExecThisPipeline(RenderContext ctx);

		/// <summary>
		/// RenderPipeline render surface. 
		/// </summary>
		private readonly RenderSurface mSurface = null;

		/// <summary>
		/// Enabled status.
		/// </summary>
		private bool mEnabled = true;
		
		#endregion
	}
}
